module tcc.dtinycc;

debug{
    import std.experimental.logger;
}


import tcc.libtcc;

class cTinyCC
{
    private{
        import std.string: toStringz;
        import std.traits:isPointer,isFunctionPointer,isDelegate;
        

        TCCState *m_pTCCState;
    }
    package:
    /// 异常弹出
    void pfThrowException(string Msg)
    {
        //throw new Exception(Msg);
        debug{
            error(Msg);
        }

    }
    public:
    this()
    {
        m_pTCCState = tcc_new();
        m_pTCCState.tcc_set_error_func(cast(void*)this,&TccException);
        
    }
    ~this()
    {
        if(m_pTCCState)
        {
            tcc_delete(m_pTCCState);
            m_pTCCState = null;
        }
    }
    /// 嵌入脚本初始化
    void pfInitScript()
    {
        pfSetOutputType(TCC_OUTPUT_MEMORY);
        pfSetOption(" -nostdlib");
        pfSetOption(" -nostdinc");
        pfSetOption(" -w");
        //pfSetOption(" -Wimplicit-function-declaration");
        /// 加载常规函数
        //pfAddSymbol("printf",printf);
        {
            static import core.stdc.stdio;
            pfAddSymbol("printf",&core.stdc.stdio.printf);
        }

        
    }

    /// 设置参数
    void pfSetOption(string opt)
    {
        tcc_set_options(m_pTCCState, opt.toStringz);
    }
    /// 增加include路径
    void pfAddIncludePath(string path)
    {
        tcc_add_include_path(m_pTCCState, path.toStringz);
    }
    /// 增加系统include路径
    void pfAddSysIncludePath(string path)
    {
        tcc_add_sysinclude_path(m_pTCCState, path.toStringz);
    }
    /// 定义一个预处理符号
    void pfDefineSymbol(string name, string value)
    {
        tcc_define_symbol(m_pTCCState, name.toStringz, value.toStringz);
    }
    /// 取消定义一个预处理符号
    void pfUndefineSymbol(string name)
    {
        tcc_undefine_symbol(m_pTCCState, name.toStringz);
    }
    /// 添加一个文件, 可以是C文件, dll, object, library, ld script
    void pfAddFile(string filename)
    {
        if(tcc_add_file(m_pTCCState, filename.toStringz) == TCC_ERR_FAIL){
            //throw new Exception("can not add file" ~ filename);
            pfThrowException("can not add file" ~ filename);
        }
    }
    /// 编译C代码字符串字符串
    void pfCompileString(string str)
    {
        if(tcc_compile_string(m_pTCCState, str.toStringz) == TCC_ERR_FAIL){
            //throw new Exception("can not compile string" ~ str);
            pfThrowException("can not compile string" ~ str);
        }
    }
    /// 设置输出类型
    void pfSetOutputType(int type)
    {
        tcc_set_output_type(m_pTCCState, type);
    }
    /// 设置库路径
    void pfSetLibPath(string path)
    {
        tcc_set_lib_path(m_pTCCState, path.toStringz);
    }
    /// 附加一个库
    void pfAddLib(string libname)
    {
        tcc_add_library(m_pTCCState, libname.toStringz);
    }
    /**
    * 增加一个已定义符号到解释器
    * Params:
    *   name: 符号名称
    *   addr: 符号内存地址
    */
    void pfAddSymbol(T)(string name, T addr)
    if(isPointer!T || isFunctionPointer!T)
    {
        tcc_add_symbol(m_pTCCState, name.toStringz, cast(void*)addr);
    }

    @disable
    void pfAddSymbol(T)(string name, T p)
    if(isDelegate!T)
    {


    }
    /// 输出一个可执行文件, 库或者对象文件, 请不要在调用tcc_relocate()之前调用
    void pfOutputFile(string filename)
    {
        if(tcc_output_file(m_pTCCState, filename.toStringz) == TCC_ERR_FAIL){
            //throw new Exception("can not output file" ~ filename);
            pfThrowException("can not output file" ~ filename);
        }
    }
    /// 链接并运行main()函数并返回其值, 请不要在调用tcc_relocate()之前调用
    void* pfRun(string[] args)
    {
        //return tcc_run(m_pTCCState, filename.toStringz);
        return null;
    }
    /// 重定位
    void pfRelocate(void* prt = TCC_RELOCATE_AUTO)
    {
        if(tcc_relocate(m_pTCCState, prt) == TCC_ERR_FAIL){
            //throw new Exception("can not relocate");
            pfThrowException("can not relocate");
        }
    }
    /// 获取一个代码中定义的符号
    T pfGetSymbol(T)(string name)
    {
        return cast(T)tcc_get_symbol(m_pTCCState, name.toStringz);
    }
}

private{
    extern(C)
    void TccException(void* opaque,in char* msg)
    {
        import std.string:fromStringz;
        import std.conv:to;

        auto pTinyC = cast(cTinyCC)opaque;
        string tmsg = msg.fromStringz.to!string;
        pTinyC.pfThrowException(tmsg);
    }
}